/****************************************************************************
**  @copyright  TinKinG
**  @contract   lsnbing@126.com
**
**  @brief  封装gumbo库，以Dom方式去操作
**  @file   gumbo_ext.cpp
**  @author TinKinG
**  @date   2016-06-07
**
**
*****************************************************************************/

#include <deque>
#include "parser.h"
#include "gumbo_ext.h"

GumboNodeList getElementsSubAll(GumboNode *node){
    GumboNodeList list;
    if(node == NULL || node->type != GUMBO_NODE_ELEMENT)
        return list;
    std::deque<GumboNode *> deq;
    GumboVector childrens = node->v.element.children;
    for(unsigned int i = 0; i < childrens.length; ++i){
        GumboNode * ac = (GumboNode *)childrens.data[i];
        if(ac->type == GUMBO_NODE_ELEMENT &&
           ac->v.element.tag != GUMBO_TAG_SCRIPT && ac->v.element.tag != GUMBO_TAG_STYLE )
            deq.push_back(ac);
    }
    GumboNode * activeNode;
    while(!deq.empty()){
        activeNode = deq.front();
        deq.pop_front();
        list.push_back(activeNode);//不需要再判断了是否为ele
        childrens = activeNode->v.element.children;
        std::deque<GumboNode*> temp_deq;                
        for(unsigned int j =0; j < childrens.length; ++j){
            GumboNode * ac = (GumboNode *)childrens.data[j];
            if(ac->type == GUMBO_NODE_ELEMENT &&
               ac->v.element.tag != GUMBO_TAG_SCRIPT && ac->v.element.tag != GUMBO_TAG_STYLE )
                temp_deq.push_back(ac);
        }
        for(int i = temp_deq.size()-1; i >= 0; --i){
            deq.push_front(temp_deq[i]);
        }
        temp_deq.clear();
    }
    return list;
}

GumboNodeList getElementsAll(GumboNode *node){
    GumboNodeList list;
    if(node == NULL || node->type != GUMBO_NODE_ELEMENT)
        return list;
    list.push_back(node);
    GumboNodeList tmp = getElementsSubAll(node);
    
    for(unsigned int i = 0; i < tmp.size(); ++i){
        list.push_back(tmp[i]);
    }
    return list;
}

GumboNodeList getElementsByAttributeName(GumboNode *node,const std::string &attribute,const std::string &value){
    GumboNodeList list = getElementsAll(node);
    GumboNodeList retList;
    if(list.empty())
        return retList;
    GumboAttribute * attr;
    GumboNode * activeNode;
    for(unsigned int i = 0; i < list.size(); ++i){
        activeNode = list[i];
        attr = gumbo_get_attribute(&activeNode->v.element.attributes,attribute.c_str());
        if(attr){
            if(attr->value == value)
                retList.push_back(activeNode);
        }
    }
    return retList;
}

GumboNodeList getElementsByClassName(GumboNode * node,const std::string & className){
    return getElementsByAttributeName(node,"class",className);
}

GumboNodeList getElementsByTagName(GumboNode *node, const std::string & tag){
    GumboNodeList list;
    std::deque<GumboNode*> deq;
    if(node == NULL || node->type != GUMBO_NODE_ELEMENT || gumbo_tag_enum(tag.c_str()) == GUMBO_TAG_UNKNOWN)//若该节点不是element元素
        return list;
    GumboVector childens = node->v.element.children;    
    for(unsigned int i = 0; i < childens.length; ++i)//全部压入栈中
        deq.push_back((GumboNode *)childens.data[i]);
    if(node->v.element.tag == gumbo_tag_enum(tag.c_str()))
        list.push_back(node);
    
    GumboNode * activeNode;
    while(!deq.empty()){
        activeNode = deq.front();
        deq.pop_front();
        if(activeNode->type != GUMBO_NODE_ELEMENT)
            continue;
        
        if(activeNode->v.element.tag == gumbo_tag_enum(tag.c_str()))
            list.push_back(activeNode);
        childens = activeNode->v.element.children;
        std::deque<GumboNode*> temp_deq;        
        for(unsigned int j = 0; j < childens.length; ++j){
            temp_deq.push_back((GumboNode *)childens.data[j]);
        }
        for(int i = temp_deq.size()-1; i >= 0; --i){
            deq.push_front(temp_deq[i]);
        }
        temp_deq.clear();
    }
    return list;
}

GumboNode * getElementByAttributeNameAtFirst(GumboNode *node,const std::string &attribute,const std::string &value){
    GumboNodeList list = getElementsByAttributeName(node,attribute,value);
    if(list.empty() )
        return NULL;
    return list[0];
}

GumboNode * getElementByClassNameAtFirst(GumboNode *node, const std::string & className){
    GumboNodeList list = getElementsByClassName(node,className);
    if(list.empty())
        return NULL;
    else
        return list[0];
}

GumboNode * getElementByTagNameAtFirst(GumboNode *node, const std::string & tag){
    GumboNodeList list = getElementsByTagName(node,tag);
    if(list.empty())
        return NULL;
    else
        return list[0];
}

GumboNode * getElementById(GumboNode *node, const std::string & value){
    return getElementByAttributeNameAtFirst(node,"id",value);
}

GumboNode * getElementByName(GumboNode *node,const std::string &value){
    return getElementByAttributeNameAtFirst(node,"name",value);
}

GumboNode * getParentNodeElement(GumboNode * node){
    GumboNode * parent = node->parent;//一个节点的父节点一定是元素节点，因为文本节点不能包含文本节点
    if(node == NULL || parent == NULL)
        return NULL;
    return parent;
}
GumboNode * getPreviousElement(GumboNode *node){
    if(node == NULL || node->type != GUMBO_NODE_ELEMENT)
        return NULL;
    GumboNode * parent = NULL;
    if(node->parent != NULL && node->parent->type == GUMBO_NODE_ELEMENT)
        parent = node->parent;
    if(parent == NULL)
        return NULL;
    GumboVector vector = parent->v.element.children;
    GumboNode * front = NULL;
    for(unsigned int i = 0; i < vector.length; ++i){
        GumboNode * activeNode = (GumboNode *)vector.data[i];
        if(activeNode->type == GUMBO_NODE_ELEMENT && activeNode == node){
            return front;
        }else if(activeNode->type == GUMBO_NODE_ELEMENT){
            front = activeNode;
        }
    }
    return front;
}

GumboNode * getNextElement(GumboNode *node){
    if(node == NULL || node->type != GUMBO_NODE_ELEMENT)
        return NULL;
    GumboNode * parent = NULL;
    if(node->parent != NULL && node->parent->type == GUMBO_NODE_ELEMENT)
        parent = node->parent;
    if(parent == NULL)
        return NULL;
    GumboVector vector = parent->v.element.children;
    bool flag = false;
    for(unsigned int i = 0; i < vector.length; ++i){
        GumboNode * activeNode = (GumboNode *)vector.data[i];
        if(flag == true && activeNode->type == GUMBO_NODE_ELEMENT)//必须判断是否为ele，因为自节点可能包含一些space节点，但是整体是有序的
            return activeNode;
        if(node == activeNode){
            flag = true;
            continue;
        }
    }
    return NULL;
}

std::string getAttribute(GumboNode * node,const std::string &attrubute){
    std::string value;
    if(node == NULL || node->type != GUMBO_NODE_ELEMENT)
        return value;
    GumboAttribute * attr = gumbo_get_attribute(&node->v.element.attributes,attrubute.c_str());
    if(attr)
        value = attr->value;
    return value;
}

std::string getInnerText(GumboNode *node){
    std::string text;
    std::deque<GumboNode*> deq;
    if(node == NULL || node->type != GUMBO_NODE_ELEMENT)
        return text;
    GumboVector childens = node->v.element.children;    
    for(unsigned int i = 0; i < childens.length; ++i)//全部压入栈中
        deq.push_back((GumboNode *)childens.data[i]);
    
    GumboNode * activeNode;
    while(!deq.empty()){
        activeNode = deq.front();
        deq.pop_front();
        if(activeNode->type == GUMBO_NODE_TEXT)
            text += activeNode->v.text.text;
        if(activeNode->type == GUMBO_NODE_ELEMENT){
            childens = activeNode->v.element.children;
            std::deque<GumboNode*> temp_deq;        
            for(unsigned int j = 0; j < childens.length; ++j){
                temp_deq.push_back((GumboNode *)childens.data[j]);
            }
            for(int i = temp_deq.size()-1; i >= 0; --i){
                deq.push_front(temp_deq[i]);
            }
            temp_deq.clear();
        }
    }
    return text;  
}

std::string getAttributeValue(GumboNode * node){
    return getAttribute(node,"value");
}

//std::string getInnerNodeText(GumboNode * node){
//    std::string text;
//    if(node == NULL || node->type != GUMBO_NODE_ELEMENT)
//        return text;
//    if(node->v.element.children.length == 0)
//        return text;
//    GumboNode * innerNode = (GumboNode*)node->v.element.children.data[0];
//    if(innerNode->type == GUMBO_NODE_TEXT)
//        text = innerNode->v.text.text;
//    return text;
//}



